<?php
Loader::import('pspframework.utils.ClassRegistry');
Loader::import('pspframework.utils.Validation');
Loader::import('pspframework.behaviors.behavior');
Loader::import('pspframework.datasource.ConnectionManager');

class Model extends Object {

/**
该model的名称，其值是比如HuiModel类的名是hui
 */
	public $name = null;

/**
该model的别名，默认的值是跟$name一样
 */
	public $alias = null;

/**
设置数据库表字段名的别名,存储时一个数值array('原来字段名'=>'现在的别名')
 */
	public $fieldsAlias = null;	
	
/**
链接什么类型的数据库
 */
	public $useDbConfig = 'default';

/**
对应的数据库表名,不含前缀
 */
	public $table = false;
//表名的前缀
	public $tablePrefix = null;		
	
/**
用来存储表名和model的别名alias的数值，key是表明，value是别名alias
 */
	public $tableToModel = array();	
	
//存主键的值	
	public $id = false;	
//存表的主键字段名
	public $primaryKey = null;	
	
//操作时存储的数据
	public $data = array();	
//存储本表的字段结构	
	public $_schema = null;
//用来存储，设置数据库字段值的过滤规则	
	public $validate = array();
//用来设置是否开启自动过滤配置	
	public $initvalidate = true;
	
//用来存储，那些字段值过滤出错的
	public $validationErrors = array();	
/**
用来设置是否开启数据库表名和表结构缓存，true表示开启，false表示关闭
 */
	public $cacheSources = true;	
	
	
/**
存储最后插入id号
 */
	public $__insertID = null;

/**
得到的 返回结果集中行的数目。此命令仅对 SELECT 语句有效。
 */
	public $__numRows = null;

/**
取得最近一次与 connection 关联的 INSERT，UPDATE 或 DELETE 查询所影响的记录行数。
 */
	public $__affectedRows = null;	
	
/**
存储哪些字段名应许被保存，其是一个数组，每个元素是字段名
 */
	public $whitelist = array();	


	public $Behaviors = array();
	
    //用来存储所有的model帮助实体
	public $BehaviorCollection = null;		

	public function __construct($id = false, $table = null, $ds = null) {
		parent::__construct();

		if (is_array($id)) {
			extract(array_merge(
				array(
					'id' => $this->id, 'table' => $this->table, 'ds' => $this->useDbConfig,
					'name' => $this->name, 'alias' => $this->alias
				),
				$id
			));
		}

		if ($this->name === null) {
			$this->name = (isset($name) ? $name : strtolower(str_replace('Model', '', get_class($this))));
		}
		if ($this->alias === null) {
			$this->alias = (isset($alias) ? $alias : $this->name);
		}

		if ($this->primaryKey === null) {
			$this->primaryKey = 'id';
		}

		ClassRegistry::addObject('models'.$this->alias, $this);

		$this->id = $id;
		unset($id);

		if ($table === false) {
			$this->table = false;
		} elseif ($table) {
			$this->table = $table;
		}

		if ($ds !== null) {
			$this->useDbConfig = $ds;
		}
		$this->BehaviorCollection = new BehaviorCollection();
		if ($this->table !== false) {	
			$this->setDataSource($ds);//连接数据库的建立
			$this->setSource($this->table);//得到和设置数据库所有表名和所有表结构	
		    //$this->initinvalidFields();//用数据库结构来初始化要过滤的字段名
		} elseif ($this->table === false) {
			$this->table = $this->name;
		}
		$this->BehaviorCollection->init($this->Behaviors);
	}
	

	
	function set($one, $two = null) {
		if (!$one) {
			return;
		}
		if (is_array($one)) {
		    if(isset($one[$this->alias])){
                $data = $one;
            }else {
                $data[$this->alias] = $one;
            }			
		} else {
			$data = array($this->alias => array($one => $two));
		}
		foreach ($data as $modelName => $fieldSet) {
			if (is_array($fieldSet)) {
				foreach ($fieldSet as $fieldName => $fieldValue) {
					if (isset($this->validationErrors[$fieldName])) {
						unset ($this->validationErrors[$fieldName]);
					}
					if(!$this->hasField($fieldName)) {
					    continue;
					}
					if ($modelName === $this->alias) {
						if ($fieldName === $this->primaryKey) {
							$this->id = $fieldValue;
						}
					}
					if (is_array($fieldValue)) {
						$fieldValue = $this->deconstruct($fieldName, $fieldValue);
					}
					$this->data[$modelName][$fieldName] = $fieldValue;
				}
			}
		}
		$this->setFieldsValue();
		return $this->data;
	}	


    //数据类型转换
	function setFieldsValue() {
	    if($this->initvalidate!==true) return false;
	    $fields = $this->schema();
		foreach($fields as $field =>$config) {
		    if(isset($this->data[$this->alias][$field])) {
				switch ($config['type']) {
					case 'string':

					break;
					case 'text':

					break;
					case 'integer':
                        $this->data[$this->alias][$field] = intval($this->data[$this->alias][$field]);
					break;
					case 'float':
                        $this->data[$this->alias][$field] = floatval($this->data[$this->alias][$field]);					
					break;	
					case 'datetime':						
					case 'timestamp':

					break;	
					case 'time':

					break;	
					case 'date':												
                       
					break;	
					case 'binary':
					break;	
					case 'boolean':
                        $this->data[$this->alias][$field] = intval($this->data[$this->alias][$field]);
					break;				
					default:				
					break;			
				}
            }				
		}
	}

	function defaultField($dataFields = array()) {
		if (empty($this->data) && !$this->hasField($dataFields)) {
			return false;
		}
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		foreach ($dataFields as $field) {
			$keyPresentAndEmpty = (
				isset($this->data[$this->alias]) &&
				array_key_exists($field, $this->data[$this->alias]) &&
				$this->data[$this->alias][$field] === null
			);
			if ($keyPresentAndEmpty) {
				unset($this->data[$this->alias][$field]);
			}
		}
		if (isset($this->data[$this->alias])) {
			$fields = array_keys($this->data[$this->alias]);
		}		
		foreach ($dataFields as $updateCol) {
			if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) {
				$default = array('formatter' => 'date');
				$colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]);
				if (!array_key_exists('format', $colType)) {
					$time = strtotime('now');
				} else {
					$time = $colType['formatter']($colType['format']);
				}
				if (!empty($this->whitelist)) {
					$this->whitelist[] = $updateCol;
				}
				$this->set($updateCol, $time);
			}
		}		
		return $this->data[$this->alias];
	}		
	
	
	function deconstruct($field, $data) {
		if (!is_array($data)) {
			return $data;
		}

		$copy = $data;
		$type = $this->getColumnType($field);
		if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) {
			$useNewDate = (isset($data['year']) || isset($data['month']) ||
				isset($data['day']) || isset($data['hour']) || isset($data['minute']));

			$dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec');
			$timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');

			$db =& ConnectionManager::getDataSource($this->useDbConfig);
			$format = $db->columns[$type]['format'];
			$date = array();

			if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) {
				$data['hour'] = $data['hour'] + 12;
			}
			if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) {
				$data['hour'] = '00';
			}
			if ($type == 'time') {
				foreach ($timeFields as $key => $val) {
					if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
						$data[$val] = '00';
					} elseif ($data[$val] === '') {
						$data[$val] = '';
					} else {
						$data[$val] = sprintf('%02d', $data[$val]);
					}
					if (!empty($data[$val])) {
						$date[$key] = $data[$val];
					} else {
						return null;
					}
				}
			}

			if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') {
				foreach ($dateFields as $key => $val) {
					if ($val == 'hour' || $val == 'min' || $val == 'sec') {
						if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
							$data[$val] = '00';
						} else {
							$data[$val] = sprintf('%02d', $data[$val]);
						}
					}
					if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) {
						return null;
					}
					if (isset($data[$val]) && !empty($data[$val])) {
						$date[$key] = $data[$val];
					}
				}
			}
			$date = str_replace(array_keys($date), array_values($date), $format);
			if ($useNewDate && !empty($date)) {
				return $date;
			}
		}
		
		return $data;
	}	
	
	//连接数据库的建立，无返回值
	function setDataSource($dataSource = null) {
		if ($dataSource != null) {
			$this->useDbConfig = $dataSource;
		}
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		if (isset($db->config['prefix'])) {
			$this->tablePrefix = $db->config['prefix'];
		}
		if (empty($db) || !is_object($db)) {
		    exit($this->alias.':missingConnection');
		}
	}	
		
	
	//得到某数据库的所有表名且将其缓存
	function setSource($tableName) {
		$this->setDataSource($this->useDbConfig);
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		$db->cacheSources = ($this->cacheSources && $db->cacheSources);
		if ($db->isInterfaceSupported('listSources')) {
			$sources = $db->listSources();
			if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
				exit($this->tablePrefix . $tableName.":missingTable");
			}
			$this->_schema = null;
		}
		$this->table = $tableName;
		$this->tableToModel[$this->table] = $this->alias;
		$this->schema();
	}	
	//设置和得到表的结构
	function schema($field = false) {
		if (!is_array($this->_schema) || $field === true) {
			$db =& ConnectionManager::getDataSource($this->useDbConfig);
			$db->cacheSources = ($this->cacheSources && $db->cacheSources);
			if ($db->isInterfaceSupported('describe') && $this->table !== false) {
				$this->_schema = $db->describe($this, $field);
			} elseif ($this->table === false) {
				$this->_schema = array();
			}
		}
		if (is_string($field)) {
			if (isset($this->_schema[$field])) {
				return $this->_schema[$field];
			} else {
				return null;
			}
		}
		return $this->_schema;
	}	
	
	//用来给字段名前没加model的别名的字段名加model别名，也就是比如把$field='name'的变成user.name并且加上数据库特有的$startQuote = "`"这个符号
	function escapeField($field = null, $alias = null) {
		if (empty($alias)) {
			$alias = $this->alias;
		}
		if (empty($field)) {
			$field = $this->primaryKey;
		}
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		if (strpos($field, $db->name($alias) . '.') === 0) {
			return $field;
		}
		return $db->name($alias . '.' . $field);
	}	
	
	//在执行过滤字段值前执行
	function beforeValidate($options = array()) {
		return true;
	}
	
	
	
    //用数据库结构来初始化要过滤的字段名
	function initinvalidFields() {
	    if($this->initvalidate!==true) return false;
	    $fields = $this->schema();
		//$db =& ConnectionManager::getDataSource($this->useDbConfig);
		foreach($fields as $field =>$config) {
		    if(!isset($this->validate[$field]) && empty($this->validate[$field])) {
				switch ($config['type']) {
					case 'string':
                        $null = 1;
                        $config['null']	&& $null = 0;					
					    $this->validate[$field] = array('between' => array('rule' => array('between', $null, $config['length']), 'message' => '字符长度为'.$null.'-'.$config['length'].'个字符.'));
					break;
					case 'text':
                        $null = 1;
                        $config['null']	&& $null = 0;					
					    $this->validate[$field] = array('between' => array('rule' => array('between', $null, 65535), 'message' => '字符长度为'.$null.'-65535个字符.'));
					break;
					case 'integer':
                        $null = 1;
                        $config['null']	&& $null = 0;					
					    $this->validate[$field] = array(						
						array('rule' => array('comparison','>=',-2147483648), 'message' => '数字大小要大于-2147483648'),
						array('rule' => array('comparison','<=',2147483647), 'message' => '数字大小要小于2147483647')
						);
					break;
					case 'float':
                        $null = 1;
                        $config['null']	&& $null = 0;	
                        if($config['length']) {
						    $length =  explode(',',$config['length']);
                            if(isset($length[1]) && !empty($length[1])) {
							    $length = $length[1];	
								$this->validate[$field][] =	array('rule' => array('decimal',$length), 'message' => '数字格式错误');								
                            }								
                        }						
					    $this->validate[$field][] = array('rule' => array('comparison','>=',-10000000000), 'message' => '数字大小要大于10000000000');
						$this->validate[$field][] = array('rule' => array('comparison','<=',10000000000), 'message' => '数字大小要小于10000000000');					
					break;	
					case 'datetime':						
					case 'timestamp':
 					    $this->validate[$field] = array(						
						array('rule' => array('custom', '/^\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}$/i'), 'message' => '时间格式错误!')
						); 
					break;	
					case 'time':
 					    $this->validate[$field] = array(						
						array('rule' => array('time'), 'message' => '时间格式错误!')
						); 
					break;	
					case 'date':												
 					    $this->validate[$field] = array(						
						array('rule' => array('date','ymd'), 'message' => '日期格式错误!')
						);                        
					break;	
					case 'binary':
					break;	
					case 'boolean':
                        $null = 1;
                        $config['null']	&& $null = 0;					
					    $this->validate[$field] = array(						
						array('rule' => array('comparison','>=',-128), 'message' => '字符大小要大于-128'),
						array('rule' => array('comparison','<=',127), 'message' => '字符大小要小于127')
						);
					break;				
					default:				
					break;			
				}
            }				
		} 
	}		
	
    //用来过滤字段名，如果某个字段有错，则会返回false
	function validates($options = array()) {
		$errors = $this->invalidFields($options);
		if (is_array($errors)) {
			return count($errors) === 0;
		}
		return $errors;
	}	

/*
 	var $validate = array('username' => array('between' => array('rule' => array('between', 5, 20), 'message' => '用户名为5-20个字符.')),
						  'password1' => array('between' => array('rule' => array('between', 5, 20), 'message' => '密码为5-20个字符.')),
						  'ckpassword' => array('rule' => array('validateConfirmPassword'),'message'=>'两次输入的密码不同.'),
						  'passwd2' => array('rule' => array('validateConfirmPasswordForChangePasswd'),'message'=>'Passwords you entered are not the same.'),
						  'email' => array('rule' => 'email', 'allowEmpty' => true, 'message' => 'Invalid Email Address.'), 
					);
*/	
    //用来过滤设置了过滤器的字段或设置好要过滤的字段名
	function invalidFields($options = array()) {
		if ($this->beforeValidate($options) === false) {
			return false;
		}
		if (!isset($this->validate) || empty($this->validate)) {
			return $this->validationErrors;
		}
		
		$data = $this->data;

		$methods = array_map('strtolower', get_class_methods($this));
		
		if (isset($data[$this->alias])) {
			$data = $data[$this->alias];
		} elseif (!is_array($data)) {
			$data = array();
		}

		$Validation =& Validation::getInstance();
		$_validate = $this->validate;//用来保存老的过滤数据
		//开始：以下是用来设置哪些字段名要过滤的
		$whitelist = $this->whitelist;
		if (!empty($options['fieldList'])) {
			$whitelist = $options['fieldList'];
		}
		if (!empty($whitelist)) {
			$validate = array();
			foreach ((array)$whitelist as $f) {
				if (!empty($this->validate[$f])) {
					$validate[$f] = $this->validate[$f];
				}
			}
			$this->validate = $validate;
		}
		//结束：以上是用来设置哪些字段名要过滤的

		foreach ($this->validate as $fieldName => $ruleSet) {
			if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
				$ruleSet = array($ruleSet);
			}
			$default = array(
				'allowEmpty' => null,//表示是否应许为空
				'required' => null,//表示如果该字段名不存在时，要不要提示错误
				'rule' => 'blank',
				'last' => false,//表示后面的要不要继续过滤
				'on' => null//表示是否开启该过滤
			);

			foreach ($ruleSet as $index => $validator) {
				if (!is_array($validator)) {
					$validator = array('rule' => $validator);
				}
				$validator = array_merge($default, $validator);

				if (isset($validator['message'])) {
					$message = $validator['message'];
				} else {
					$message = 'This field cannot be left blank';
				}

				if (empty($validator['on']) || ($validator['on'] == 'create' ) || ($validator['on'] == 'update' )) {
					$required = (
						(!isset($data[$fieldName]) && $validator['required'] === true) ||
						(
							isset($data[$fieldName]) && (empty($data[$fieldName]) &&
							!is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false
						)
					);
					if ($required) {
						/*有两种情况，1表示假如不存在该字段名时且需要显示错误信息（即$validator['required'] === true）时,
						2假如存在字段名时并且为空而且此时不应许为空时
						*/
						$this->invalidate($fieldName, $message);
						if ($validator['last']) {
							break;
						}
					} elseif (array_key_exists($fieldName, $data)) {
						if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) {
							break;
						}
						if (is_array($validator['rule'])) {
							$rule = $validator['rule'][0];
							unset($validator['rule'][0]);
							$ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule']));
						} else {
							$rule = $validator['rule'];
							$ruleParams = array($data[$fieldName]);
						}

						$valid = true;

						if (in_array(strtolower($rule), $methods)) {
							$ruleParams[] = $validator;
							$ruleParams[0] = array($fieldName => $ruleParams[0]);
							$valid = $this->dispatchMethod($rule, $ruleParams);
						} elseif (method_exists($Validation, $rule)) {
							$valid = $Validation->dispatchMethod($rule, $ruleParams);
						} elseif (!is_array($validator['rule'])) {
							$valid = preg_match($rule, $data[$fieldName]);
						} elseif (Param::read('debug') > 0) {
							trigger_error(sprintf('Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING);
						}

						if (!$valid || (is_string($valid) && strlen($valid) > 0)) {
							if (is_string($valid) && strlen($valid) > 0) {
								$validator['message'] = $valid;
							} elseif (!isset($validator['message'])) {
								if (is_string($index)) {
									$validator['message'] = $index;
								} elseif (is_numeric($index) && count($ruleSet) > 1) {
									$validator['message'] = $index + 1;
								} else {
									$validator['message'] = $message;
								}
							}
							$this->invalidate($fieldName, $validator['message']);

							if ($validator['last']) {
								break;
							}
						}
					}
				}
			}
		}
		$this->validate = $_validate;
		return $this->validationErrors;
	}	
/*
存储错误字段信息
*/	
	function invalidate($field, $value = true) {
		if (!is_array($this->validationErrors)) {
			$this->validationErrors = array();
		}
		$this->validationErrors[$field] = $value;
	}	
	
/**
判断在该model表中是否存在该字段名
 */
	function hasField($name) {
		if (is_array($name)) {
			foreach ($name as $n) {
				if ($this->hasField($n)) {
					return $n;
				}
			}
			return false;
		}
		if (empty($this->_schema)) {
			$this->schema();
		}

		if ($this->_schema != null) {
			return isset($this->_schema[$name]);
		}
		return false;
	}	
	
	function getColumnTypes() {
		$columns = $this->schema();
		if (empty($columns)) {
			trigger_error('(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()', E_USER_WARNING);
		}
		$cols = array();
		foreach ($columns as $field => $values) {
			$cols[$field] = $values['type'];
		}
		return $cols;
	}	
/**
得到某个字段的类型
 */
	function getColumnType($column) {
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		$cols = $this->schema();
		$model = null;

		$column = str_replace(array($db->startQuote, $db->endQuote), '', $column);

		if (strpos($column, '.')) {
			list($model, $column) = explode('.', $column);
		}
		if ($model != $this->alias && isset($this->{$model})) {
			return $this->{$model}->getColumnType($column);
		}
		if (isset($cols[$column]) && isset($cols[$column]['type'])) {
			return $cols[$column]['type'];
		}
		return null;
	}	
	//得到关键字段的值
	function getID($list = 0) {
		if (empty($this->id)) {
			return false;
		}
        return $this->id;
	}
	
/**
返回最后插入的记录的id号
 */
	function getLastInsertID() {
		return $this->getInsertID();
	}

	function getInsertID() {
		return $this->__insertID;
	}

/**
设置最后插入的记录的id号
 */
	function setInsertID($id) {
		$this->__insertID = $id;
	}

/**
返回结果集中行的数目。此命令仅对 SELECT 语句有效
 */
	function getNumRows() {
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		return $db->lastNumRows();
	}

/**
取得最近一次与 connection 关联的 INSERT，UPDATE 或 DELETE 查询所影响的记录行数。
 */
	function getAffectedRows() {
		$db =& ConnectionManager::getDataSource($this->useDbConfig);
		return $db->lastAffected();
	}	
/*
得到数据源
*/
	function &getDataSource() {
		$db =& ConnectionManager::getDataSource($this->useDbConfig);		
		return $db;
	}	
	
	function call__($method, $params) {
		$result = $this->BehaviorCollection->dispatchMethod($method, $params);
		if ($result !== array('unhandled')) {
			return $result;
		}			
 		if (!method_exists($this, $method)) {
			trigger_error(sprintf('Mode method handler '.$method.' not defined in %s', get_class($this)), E_USER_ERROR);
		}       
		return $this->{$method}($params);
	}
/*
	function __call($method, $params) {
		if (!method_exists($this, 'call__')) {
			trigger_error(sprintf('Magic method handler call__ not defined in %s', get_class($this)), E_USER_ERROR);
		}
		return $this->call__($method, $params);
	}	
	function __get($name) {
		return $this->get__($name);
	}	
	function __set($name, $value) {
		return $this->set__($name, $value);
	}	*/
}